use crate::skill::Skill;
use count_enum::iter_each;
use power_map::{MapKey, TotalMap};
use std::{cell::Cell, cmp::Ordering, marker::PhantomData};

#[derive(Clone, Copy)]
pub enum Conseq<T> {
    NotEnough,
    Bad,
    First,
    Normal(T),
}

impl<T> Conseq<T> {
    pub fn normal(&self) -> Option<&T> {
        match self {
            Self::Normal(x) => Some(x),
            _ => None,
        }
    }
    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Conseq<U> {
        use Conseq::*;
        match self {
            Normal(x) => Normal(f(x)),
            NotEnough => NotEnough,
            Bad => Bad,
            First => First,
        }
    }
}

/// [`Strategy`]的“形状”，缓存用以查找
///
/// 一个类型恰好对应一个[`Shape`]。
/// 更好的做法应该是const，但做不到。
pub struct Shape<S: MapKey> {
    max_cost: usize,
    len: usize,
    layout: Box<[TotalMap<S, Conseq<usize>>]>,
}

impl<S: Skill> Shape<S> {
    pub fn new() -> Self {
        let mut len = 0;
        let max_cost = iter_each().map(S::cost).chain(std::iter::once(0)).max(); // 非空非负
        let max_cost = max_cost.unwrap().try_into().unwrap();
        let layout = (0..=max_cost)
            .flat_map(|my| std::iter::repeat(my).zip(0..=max_cost))
            .map(|(my, their)| {
                let weak = |s: S| {
                    S::all_enough(my).any(|than| {
                        let order = |res| match res {
                            Some(true) => 2,
                            Some(false) => 0,
                            None => 1,
                        };
                        let result =
                            S::all_enough(their).map(|on| match (s.versus(on), than.versus(on)) {
                                (None, None) => s.cost().cmp(&than.cost()).reverse(),
                                (this, that) => order(this).cmp(&order(that)),
                            });
                        result.clone().all(Ordering::is_le) && result.clone().any(Ordering::is_lt)
                    })
                };
                let mut first = true;
                TotalMap::from_fn(|skill: S| {
                    if !skill.enough(my) {
                        Conseq::NotEnough
                    } else if weak(skill) {
                        Conseq::Bad
                    } else if first {
                        first = false;
                        Conseq::First
                    } else {
                        len += 1;
                        Conseq::Normal(len - 1)
                    }
                })
            })
            .collect();
        Shape {
            max_cost,
            len,
            layout,
        }
    }

    fn visit(&self, my: usize, their: usize) -> TotalMap<S, Conseq<usize>> {
        let (my, their) = (my.min(self.max_cost), their.min(self.max_cost));
        self.layout[my * (self.max_cost + 1) + their].clone()
    }

    pub fn max_cost(&self) -> usize {
        self.max_cost
    }
}

#[derive(Clone)]
pub struct Strategy<T, S> {
    data: Box<[T]>,
    _marker: PhantomData<S>,
}

impl<T, S: Skill> Strategy<T, S> {
    pub fn from_fn(shape: &Shape<S>, mut f: impl FnMut() -> T) -> Self {
        Self::from_data((0..shape.len).map(|_| f()).collect())
    }

    /// 未检查长度合规
    fn from_data(data: Box<[T]>) -> Self {
        let _marker = PhantomData;
        Strategy { data, _marker }
    }

    pub fn data(&self) -> &[T] {
        &self.data
    }
    pub fn data_mut(&mut self) -> &mut [T] {
        &mut self.data
    }

    pub fn get(&self, shape: &Shape<S>, my: usize, their: usize) -> TotalMap<S, Conseq<&T>> {
        shape.visit(my, their).map(|x| x.map(|idx| &self.data[idx]))
    }
    /// 直接获取可变引用很麻烦！
    pub fn get_cell(
        &mut self,
        shape: &Shape<S>,
        my: usize,
        their: usize,
    ) -> TotalMap<S, Conseq<&Cell<T>>> {
        let data = Cell::from_mut(&mut *self.data).as_slice_of_cells();
        shape.visit(my, their).map(|x| x.map(|idx| &data[idx]))
    }
}
